package com.tangula.voicenote.service

import android.annotation.SuppressLint
import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.PixelFormat
import android.os.Binder
import android.os.IBinder
import android.util.DisplayMetrics
import android.util.Log
import android.view.*
import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener
import android.view.Gravity.LEFT
import android.widget.EditText
import com.tangula.android.base.ForeServiceNotificationBuilder
import com.tangula.android.base.TglService
import com.tangula.android.utils.ApplicationUtils
import com.tangula.android.utils.SdkVersionDecides
import com.tangula.android.utils.TaskUtils
import com.tangula.voicenote.R
import com.tangula.voicenote.VoiceNoteApplication
import com.tangula.android.utils.ToastUtils
import com.tangula.voicenote.entity.NoticeEntity
import com.tangula.voicenote.view.NoteViewHolder
import org.apache.commons.lang3.StringUtils


@Suppress("UNREACHABLE_CODE", "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS", "NAME_SHADOWING")
class AddOverlayService : Service() {

    companion object {

        const val KEY_NOTE_TYPE = "type"
        const val KEY_NOTE_ID = "noteId"
        const val NOTE_TYPE_CREATE = "create"
        const val NOTE_TYPE_REMOVE = "remove"


        fun startAddOverlayService() {
            ApplicationUtils.APP.apply { startService(Intent(this, AddOverlayService::class.java)) }
        }


        fun addOverlayService() {
            ApplicationUtils.APP.apply { startService(Intent(this, AddOverlayService::class.java)) }
        }


        fun addOverlayServiceById(id: String) {
            ApplicationUtils.APP.apply {
                startService(Intent(this, AddOverlayService::class.java).apply {
                    putExtra(
                        KEY_NOTE_TYPE,
                        NOTE_TYPE_CREATE
                    )
                    putExtra(KEY_NOTE_ID, id)
                })
            }
        }

        fun removeOverlayService(note: NoticeEntity) {
            ApplicationUtils.APP.apply {
                startService(Intent(this, AddOverlayService::class.java).apply {
                    putExtra(
                        KEY_NOTE_TYPE,
                        NOTE_TYPE_REMOVE
                    )
                    putExtra(KEY_NOTE_ID, note.id)
                })
            }
        }

    }

    override fun onBind(intent: Intent): IBinder {
        return Binder()
    }


    private var curX: Float = 0.0f
    private var curY: Float = 0.0f


    private lateinit var windowManager: WindowManager

    val map = mutableMapOf<View, WindowManager.LayoutParams>()

    override fun onCreate() {
        super.onCreate()
        windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.v("console", "start service")

        this.startForeground(0, ForeServiceNotificationBuilder().buildNotification(ApplicationUtils.APP, R.mipmap.add, "Notes", "Notes"))

        intent?.also {

            when (it.getStringExtra(KEY_NOTE_TYPE)) {
                NOTE_TYPE_REMOVE -> {
                    val noteId = it.getStringExtra(KEY_NOTE_ID)
                    removeNoteById(noteId)
                }
                else -> {
                    if (it.hasExtra(KEY_NOTE_ID)) {
                        val noteId: String? = it.getStringExtra(KEY_NOTE_ID)
                        addNewNote(noteId)
                    } else {
                        addNewNote(null)
                    }
                }
            }

        }
        return super.onStartCommand(intent, flags, startId)
    }

    private fun removeNoteById(noteId: String) {
        removeNoteById(noteId) {}
    }

    @SuppressLint("CheckResult")
    fun removeNoteById(noteId: String?, onRemoved: (() -> Unit)?) {
        noteId?.run {
            var target: View? = null
            var entity: NoticeEntity? = null
            for (cur in map.keys) {
                entity = cur.getTag(R.string.tag_note_entity) as NoticeEntity
                if (StringUtils.equals(noteId, entity.id)) {
                    target = cur
                    break
                }
            }

            target?.let { self ->
                entity?.also { ent ->
                    DatabaseService.exec { db ->
                        db.removeNotice(ent)
                        map.remove(self)
                        windowManager.removeView(self)
                        onRemoved?.also {
                            it()
                        }
                    }
                }
            } ?: run {
                onRemoved?.also { it() }
            }

        } ?: run {
            onRemoved?.also { it() }
        }
    }


    private fun addNewNote(noteId: String?) {
        removeNoteById(noteId) {
            DatabaseService.exec { db ->
                if (StringUtils.isBlank(noteId)) {
                    db.createNotice()
                } else {
                    db.byNoticeId(noteId!!) ?: db.createNotice()
                }.apply {
                    when (dockStatus) {
                        0 -> addNormal(this)
                        else -> addDocker(this)
                    }
                }
            }
        }
    }

    @SuppressLint("RtlHardcoded")
    private fun addDocker(entity: NoticeEntity) {

        val layoutParams = createLayoutParamsDocker()

        layoutParams.width = 100
        layoutParams.height = 100

        layoutParams.x = 0
        layoutParams.y = entity.top

        layoutParams.alpha = 0.5f
        layoutParams.gravity = Gravity.TOP or LEFT

        val view = LayoutInflater.from(ApplicationUtils.APP)
            .inflate(
                NoteViewHolder.noticeColor2DockerLayoutResId(entity.color % VoiceNoteApplication.COLOR_COUNT),
                null
            )
        val gd = GestureDetector(view.context, object : GestureDetector.SimpleOnGestureListener() {
            //上下移动
            override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float): Boolean {
                e2?.also { ev ->
                    layoutParams.y = ev.rawY.toInt()
                    windowManager.updateViewLayout(view, layoutParams)
                }
                return false
            }
            override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
                //向右侧滑动时恢复
                if (velocityX > 10) {
                    entity.dockStatus = 0
                    TaskUtils.runInBackground {
                        DatabaseService.exec { db ->
                            db.saveNotice(entity)
                            windowManager.removeView(view)
                            addOverlayServiceById(entity.id)
                        }
                    }
                } else {
                    Log.v("console", "docker fly:$velocityX")
                }
                return true
            }
        })
        view.setOnTouchListener { _, ev ->
            gd.onTouchEvent(ev)
            true
        }
        windowManager.addView(view, layoutParams)
    }


    @SuppressLint("ClickableViewAccessibility")
    private fun addNormal(entity: NoticeEntity) {
        NoteViewHolder(entity).apply {
            val layoutParams = createLayoutParams()

            layoutParams.x = entity.left
            layoutParams.y = entity.top

            view.setTag(R.string.tag_note_entity, this.notice)
            val text = view.findViewById<EditText>(R.id.editText)
            text.isFocusable = true

            val gd = GestureDetector(view.context, object : GestureDetector.SimpleOnGestureListener() {
                override fun onFling(
                    e1: MotionEvent?,
                    e2: MotionEvent?,
                    velocityX: Float,
                    velocityY: Float
                ): Boolean {

                    if (velocityX < 10) {
                        //to left
                        entity.dockStatus = 2
                        entity.dockOffset = entity.top

                        map.remove(view)
                        windowManager.removeView(view)

                        DatabaseService.exec { db ->
                            db.saveNotice(entity)
                            addOverlayServiceById(entity.id)
                        }
                    } else {
                        //to right
                        //ignore
                    }
                    return true
                }
            })
            val dm = DisplayMetrics()
            windowManager.defaultDisplay.getMetrics(dm)
            val phoneX =dm.widthPixels
            val phoneY = dm.heightPixels

            val sg = ScaleGestureDetector(view.context,object : ScaleGestureDetector.SimpleOnScaleGestureListener(){

                override fun onScale(detector: ScaleGestureDetector?): Boolean {
                    if(detector==null){
                        return false
                    }
                    val f = detector.currentSpan-detector.previousSpan
                    var res = false

                    if(f<=-5){
                        when(layoutParams.width){
                            in 0 .. 500->{
                                layoutParams.width=500
                            }else->{
                                layoutParams.width += f.toInt()
                                layoutParams.x -= (f/2).toInt()
                            }
                        }
                        when(layoutParams.height){
                            in 0 .. 500->{
                                layoutParams.height=500
                            }else->{
                            layoutParams.height += f.toInt()
                            layoutParams.y -= (f/2).toInt()
                        }
                        }

                        windowManager.updateViewLayout(view,layoutParams)
                        res =true
                    }else if(f>=5){
                        when(layoutParams.width){
                            in 500 .. phoneX->{
                                layoutParams.width+= f.toInt()
                                layoutParams.x -=  (f/2).toInt()
                            }
                        }

                        when(layoutParams.height){
                            in 500 .. phoneY->{
                                layoutParams.height+=f.toInt()
                                layoutParams.y -=  (f/2).toInt()
                            }
                        }

                        windowManager.updateViewLayout(view,layoutParams)
                        res =true
                    }

                    return res
                }
            })

            var latestX = 0.0f
            var latestY = 0.0f

            view.setOnTouchListener { vw, event ->
                var res = true
                val action = event.action

                if (action == MotionEvent.ACTION_DOWN) {

                    layoutParams.flags = WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    windowManager.updateViewLayout(view, layoutParams)

                    curX = event.rawX - layoutParams.x
                    curY = event.rawY - layoutParams.y

                    latestX = event.rawX
                    latestY = event.rawY

                    for (key in map.keys) {
                        val lp = map[key]
                        lp?.also {
                            if (key == view) {
                                it.alpha = 1.0f
                                key.isFocusable = true
                            } else {
                                it.alpha = 0.5f
                            }
                            key.post {
                                windowManager.updateViewLayout(key, it)
                            }
                        }
                    }

                    sg.onTouchEvent(event)
                } else if (action == MotionEvent.ACTION_MOVE) {
                    if(event.pointerCount==1) {
                        layoutParams.x = (event.rawX - curX).toInt()
                        layoutParams.y = (event.rawY - curY).toInt()
                        windowManager.updateViewLayout(vw, layoutParams)
                    }else{
                        val cx = event.rawX
                        val cy = event.rawY

                        if(Math.abs(cx-latestX)>5||Math.abs(cy-latestY)>5){
                            if(sg.onTouchEvent(event)){
                                latestX = cx
                                latestY = cy
                            }
                        }
                    }
                } else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {

                    if(event.pointerCount==1) {
                        Log.v("console", "actoin up ${view.hashCode()}")
                        if (layoutParams.alpha != 1.0f) {
                            layoutParams.alpha = 1.0f
                            windowManager.removeViewImmediate(view)
                            windowManager.addView(view, layoutParams)
                            text.isFocusable = true
                            text.requestFocus()
                            text.setSelection(text.text.length)
                        }
                        TaskUtils.runInBackground {
                            DatabaseService.exec { db ->
                                entity.top = layoutParams.y
                                entity.left = layoutParams.x
                                db.saveNotice(entity)
                            }
                        }
                    }
                } else {
                    Log.v("console", "actoin out ${view.hashCode()}")

                    layoutParams.flags = WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    layoutParams.alpha = 0.5f
                    windowManager.updateViewLayout(view, layoutParams)
                    res = false
                }
                res
            }

            text.setOnTouchListener { _, ev ->
                if(ev.pointerCount==1) {
                    gd.onTouchEvent(ev)
                }else {
                    when (ev.action) {
                        MotionEvent.ACTION_DOWN -> {
                            latestX = ev.rawX
                            latestY = ev.rawY
                            sg.onTouchEvent(ev)
                        }
                        MotionEvent.ACTION_MOVE -> {
                            if (ev.pointerCount == 1) {
                                layoutParams.x = (ev.rawX - curX).toInt()
                                layoutParams.y = (ev.rawY - curY).toInt()
                                windowManager.updateViewLayout(view, layoutParams)
                            } else {
                                val cx = ev.rawX
                                val cy = ev.rawY

                                if (Math.abs(cx - latestX) > 5 || Math.abs(cy - latestY) > 5) {
                                    if(sg.onTouchEvent(ev)){
                                        latestX = cx
                                        latestY = cy
                                    }
                                }
                            }
                        }
                    }
                }
                false
            }

            text.setOnClickListener {
                Log.v("console", "remove ${view.hashCode()}")
                if (layoutParams.alpha != 1.0f) {
                    windowManager.removeViewImmediate(view)
                    layoutParams.alpha = 1.0f
                    layoutParams.flags = WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
                            WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    //layoutParams.flags = layoutParams.flags or WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
                    text.isFocusable = true
                    windowManager.addView(view, layoutParams)
                }
                text.requestFocus()
                text.isFocusable = true
            }

            layoutParams.alpha = 0.5f
            layoutParams.gravity = Gravity.TOP or Gravity.START

            map[view] = layoutParams
            windowManager.addView(view, layoutParams)
        }
    }


    @SuppressLint("InlinedApi")
    private fun createLayoutParams(): WindowManager.LayoutParams {
        return WindowManager.LayoutParams(
                600,
                600,
                if(SdkVersionDecides.beforeSdk25A7d1d1()) WindowManager.LayoutParams.TYPE_PHONE else  WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, //WindowManager.LayoutParams.TYPE_PHONE, //
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                PixelFormat.TRANSLUCENT
            )
    }

    @SuppressLint("InlinedApi")
    private fun createLayoutParamsDocker(): WindowManager.LayoutParams {
        return WindowManager.LayoutParams(
            600,
            600,
            if(SdkVersionDecides.beforeSdk25A7d1d1()) WindowManager.LayoutParams.TYPE_PHONE else  WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, //WindowManager.LayoutParams.TYPE_PHONE, //
            WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            PixelFormat.TRANSLUCENT
        )
    }


}
